#include "Backend.h"
#include "Utils.h"
#include "CLogin.h"
#include <utility>
#include <algorithm>
#include <exception>
#include "CFollowersQueryInc.h"

ThreadManager::ThreadManager()
{

}

ThreadManager::~ThreadManager()
{

}

ThreadManager& ThreadManager::GetInstance()
{
    static ThreadManager mgr;
    return mgr;
}

void ThreadManager::RegThread(wxThread* pThread)
{
    wxCriticalSectionLocker lockList(threadsList_CS);
    threadsList.push_back(pThread);
}

void ThreadManager::UnregThread(wxThread* pThread)
{
    wxCriticalSectionLocker lockList(threadsList_CS);
    std::vector<wxThread*>::iterator resaultItr;
    resaultItr = std::find(threadsList.begin(), threadsList.end(), pThread);
    if(resaultItr!=threadsList.end()) threadsList.erase(resaultItr);
}

void ThreadManager::cleanAllThreads()
{
    std::vector<wxThread*>::iterator itr;
    for(itr = threadsList.begin(); itr!=threadsList.end();++itr)
    {
        //if(! (*itr)->IsRunning())
        if(*itr != NULL) continue;

        wxThreadError errorCode = (*itr)->Delete();
        if(errorCode != wxTHREAD_NO_ERROR)
        {
            #ifdef MT_LOG
                LOGMAN.logError(wxString(_T("In cleaning of unfinished threads, thread error :"))<<errorCode<<wxString(_T("from delete")));
            #endif
        }
        (*itr)= NULL;
    }

    threadsList.clear();
}



BackendWorker_GetPIN::BackendWorker_GetPIN(wxEvtHandler* mainThread_handler, wxCriticalSection* dataCS_handler)
:wxThread(wxTHREAD_DETACHED)
,m_pMainThread(mainThread_handler)
,m_pCLoginMainThreadCS(dataCS_handler)
,m_pLogin(NULL)
{
    THREADMAN.RegThread(this);
}

BackendWorker_GetPIN::~BackendWorker_GetPIN()
{
    m_pMainThread = NULL;
    m_pCLoginMainThreadCS = NULL;
    THREADMAN.UnregThread(this);
}

bool BackendWorker_GetPIN::startWorking(CLogin* pLogin)
{
    wxThreadError threadReturnCode = Create();

    if (threadReturnCode!= wxTHREAD_NO_ERROR)
    {
        #ifdef MT_LOG
        LOGMAN.logError(wxString(_T("Can't create the thread to get PIN , error code "))<< threadReturnCode);
        #endif
        return false;
    }
    else
    {
        m_pLogin = pLogin;
        threadReturnCode = Run();
        if (threadReturnCode != wxTHREAD_NO_ERROR )
        {
            #ifdef MT_LOG
            LOGMAN.logError(wxString(_T("Can't run the thread to get PIN, error code "))<< threadReturnCode);
            #endif
            return false;
        }
    }

    return true;
}

wxThread::ExitCode BackendWorker_GetPIN::Entry()
{
    wxCriticalSectionLocker CF_DB_Locker(*m_pCLoginMainThreadCS);
    #ifdef MT_DEBUG_LOG
    LOGMAN.logInfo("[MT] Enter CS in login PIN request");
    #endif

    wxThreadEvent* doneEvt =new wxThreadEvent(EVT_GET_PIN_DONE);

    if(m_pLogin==NULL)
    {
        #ifdef MT_LOG
        LOGMAN.logError("m_pLogin is NULL in BackendWorker_GetPIN entry point");
        #endif
        doneEvt->SetPayload<uint8_t>(CLogin::UNKNOWN_ERROR);
    }
    else
    {
        m_pLogin->getToken(CLogin::REQUEST_TOKEN);  //using errorCode to identify OK , bool not used
        doneEvt->SetPayload<uint8_t>(m_pLogin->getErrorCode());
    }

    wxQueueEvent(m_pMainThread, doneEvt);
    doneEvt = NULL;
    return (wxThread::ExitCode)0;
}

///---------------------------------------------------------------------------------------

BackendWorker_Login::BackendWorker_Login(wxEvtHandler* mainThread_handler, wxCriticalSection* dataCS_handler)
:wxThread(wxTHREAD_DETACHED)
,m_pMainThread(mainThread_handler)
,m_pCLoginMainThreadCS(dataCS_handler)
,m_pLogin(NULL)
{
    THREADMAN.RegThread(this);
}

BackendWorker_Login::~BackendWorker_Login()
{
    m_pMainThread = NULL;
    m_pCLoginMainThreadCS = NULL;
    THREADMAN.UnregThread(this);
}

bool BackendWorker_Login::startWorking(CLogin* pLogin, const wxString& PIN)
{
    wxThreadError threadReturnCode = Create();

    if (threadReturnCode!= wxTHREAD_NO_ERROR)
    {
        #ifdef MT_LOG
        LOGMAN.logError(wxString(_T("Can't create the thread to submit PIN , error code "))<< threadReturnCode);
        #endif
        return false;
    }
    else
    {
        m_pLogin = pLogin;
        m_PIN = PIN;
        threadReturnCode = Run();
        if (threadReturnCode != wxTHREAD_NO_ERROR )
        {
            #ifdef MT_LOG
            LOGMAN.logError(wxString(_T("Can't run the thread to submit PIN, error code "))<< threadReturnCode);
            #endif
            return false;
        }
    }

    return true;
}

wxThread::ExitCode BackendWorker_Login::Entry()
{
    wxCriticalSectionLocker CF_DB_Locker(*m_pCLoginMainThreadCS);
    #ifdef MT_DEBUG_LOG
    LOGMAN.logInfo("[MT] Enter CS in login PIN request");
    #endif

    wxThreadEvent* doneEvt =new wxThreadEvent(EVT_LOGIN_DONE);

    if(m_pLogin==NULL)
    {
        #ifdef MT_LOG
        LOGMAN.logError("m_pLogin is NULL in BackendWorker_Login entry point");
        #endif
        doneEvt->SetPayload<uint8_t>(CLogin::UNKNOWN_ERROR);
    }
    else if(m_PIN.IsEmpty())
    {
        #ifdef MT_LOG
        LOGMAN.logError("PIN code is empty in BackendWorker_Login entry point");
        #endif
        doneEvt->SetPayload<uint8_t>(CLogin::UNKNOWN_ERROR);
    }
    else
    {
        if(m_pLogin->getToken(CLogin::ACCESS_TOKEN, m_PIN)) //using errorCode to identify OK , bool not used
        {
            m_pLogin->getUserInfo(VERIFY_ACCESS_TOKEN_API);
        }
        doneEvt->SetPayload<uint8_t>(m_pLogin->getErrorCode());
    }

    wxQueueEvent(m_pMainThread, doneEvt);
    doneEvt = NULL;
    return (wxThread::ExitCode)0;
}




///-----------------------------------------------------------------------------------------------------------



BackendWorker_CFsync::BackendWorker_CFsync(wxEvtHandler* mainThread_handler, wxCriticalSection* dataCS_handler)
:wxThread(wxTHREAD_DETACHED)
,m_pMainThread(mainThread_handler)
,m_pCFMainThreadCS(dataCS_handler)
,resultFlag(0x0)
{
    THREADMAN.RegThread(this);
}

BackendWorker_CFsync::~BackendWorker_CFsync()
{
    m_pMainThread = NULL;
    m_pCFMainThreadCS = NULL;
    THREADMAN.UnregThread(this);
}

bool BackendWorker_CFsync::startWorking(CFollowersQueryInc* CF_in, wxString& userId_in)
{
    m_CF = CF_in;
    m_userID = userId_in;
    if(m_CF==NULL) return false;

    wxThreadError threadReturnCode = Create();

    if (threadReturnCode!= wxTHREAD_NO_ERROR)
    {
        #ifdef MT_LOG
        LOGMAN.logError(wxString(_T("Can't create the thread for CF sync, error code "))<< threadReturnCode);
        #endif
        return false;
    }
    else
    {
        threadReturnCode = Run();
        if (threadReturnCode != wxTHREAD_NO_ERROR )
        {
            #ifdef MT_LOG
            LOGMAN.logError(wxString(_T("Can't run the thread for CF sync, error code "))<< threadReturnCode);
            #endif
            return false;
        }
    }

    return true;
}

wxThread::ExitCode BackendWorker_CFsync::Entry()
{
    wxCriticalSectionLocker CFlocker(*m_pCFMainThreadCS);
    #ifdef MT_DEBUG_LOG
        LOGMAN.logInfo("[MT] Enter CS in CF sync OnButtonClick");
    #endif

    assert(m_CF!=NULL);

    wxThreadEvent* doneEvt =new wxThreadEvent(EVT_CF_SYNC_DONE);

    LOGMAN.resetErrorCount();
    try
    {
        if(m_CF->initRecordData(m_userID))
        {
            //INIT_RECORD_DATA_OK
            resultFlag = resultFlag | (1<<INIT_RECORD_DATA_OK);

            if(m_CF->find_unfollowed(m_userID))
            {
                //FIND_UNFOLLOWED_OK
                resultFlag = resultFlag | (1<<FIND_UNFOLLOWED_OK);
                if(m_CF->update_new_followed(m_userID))
                {
                    //UPDATE_NEW_FOLLOWED_OK
                    resultFlag = resultFlag | (1<<UPDATE_NEW_FOLLOWED_OK);
                }

                if(LOGMAN.hasAnyError()) throw std::runtime_error("Over all process unfinished");

                //FINAL_OK
                resultFlag = resultFlag | (1<<FINAL_OK);
                doneEvt->SetPayload<int8_t>(resultFlag);
                wxQueueEvent(m_pMainThread, doneEvt);
                return (wxThread::ExitCode)0;

            }
            else
            {
                //Unfinished errors only appears in false branch
                if(LOGMAN.hasAnyError()) throw std::runtime_error("Over all process unfinished");
                if(m_CF->update_new_followed(m_userID))
                {
                    //UPDATE_NEW_FOLLOWED_OK
                    resultFlag = resultFlag | (1<<UPDATE_NEW_FOLLOWED_OK);
                    doneEvt->SetPayload<int8_t>(resultFlag);
                    wxQueueEvent(m_pMainThread, doneEvt);
                    return (wxThread::ExitCode)0;
                }
                else
                {
                    //NOTHING_MORE
                    if(LOGMAN.hasAnyError()) throw std::runtime_error("Over all process unfinished");
                    resultFlag = resultFlag | (1<<NOTHING_MORE);
                    doneEvt->SetPayload<int8_t>(resultFlag);
                    wxQueueEvent(m_pMainThread, doneEvt);
                    return (wxThread::ExitCode)0;
                }
            }// End of if(find_unfollowed)

        }
        else
        {
            if(LOGMAN.hasAnyError()) throw std::runtime_error("Over all process unfinished");
            doneEvt->SetPayload<int8_t>(resultFlag);
            wxQueueEvent(m_pMainThread, doneEvt);
            return (wxThread::ExitCode)0;
        }// End of if(initRecordData)
    }catch(...)
    {
        //HAS_ANY_ERROR
        resultFlag = resultFlag | (1<<HAS_ANY_ERROR);
        doneEvt->SetPayload<int8_t>(resultFlag);
        wxQueueEvent(m_pMainThread, doneEvt);
        return (wxThread::ExitCode)0;
    }

    #ifdef MT_LOG
    LOGMAN.logError("CF sync flow control goes into a situation which should not happens.");
    #endif
    return (wxThread::ExitCode)0;
}
